home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c++-part1 / 5520 < prev    next >
Encoding:
Internet Message Format  |  1996-08-05  |  5.8 KB

  1. Path: dawn.mmm.com!news
  2. From: kjhopps@mmm.com (Kevin J Hopps)
  3. Newsgroups: comp.lang.c++
  4. Subject: Re: How to handle error in constructor
  5. Date: 5 Feb 1996 13:49:56 GMT
  6. Organization: 3M - St. Paul, MN  55144-1000 US
  7. Message-ID: <4f51u4$2oq@dawn.mmm.com>
  8. References: <DLyyIM.5EG@teslab.lab.oz.au> <4enbts$35e@tofu.alt.net>
  9. Reply-To: kjhopps@mmm.com
  10. X-Newsreader: TIN [version 1.2 PL2]
  11. X-Newsreader: TIN [version 1.2 PL2]
  12. X-Newsreader: TIN [version 1.2 PL2]
  13. X-Newsreader: TIN [version 1.2 PL2]
  14.  
  15. Walt Howard (walth@netcom.com) wrote:
  16. > On Tue, 30 Jan 1996 00:54:21 GMT, andrew@teslab.lab.oz.au (Andrew
  17. > Phillips) wrote:
  18.  
  19. > >I'm converting and enhancing a simple program in C to C++.  Without going
  20. > >into too much detail I have a class that represents a file on disk.  The
  21. > >contructor opens the file, but what should it do if the file cannot be
  22. > >opened?  The C program just calls fopen() tests the return value and 
  23. > >if there's an error it prints a message and exits.
  24. > >
  25. > >In C++ I thought to set a flag in the constructor and have a member function
  26. > >that tests the flag to see if the file was opened correctly.  This seems
  27. > >rather inelegant -- I guess exceptions would be the elegant way but seem
  28. > >like overkill for such a simple program.  I've looked in several C++ books but
  29. > >error handling is given scant coverage except for exception handling.
  30. > >
  31. > >Any suggestions would be greatly appreciated.
  32. > >
  33.  
  34. > There are several approaches. The one you came up with is similar to
  35. > what the C++ stream library does. However, that was written before
  36. > exceptions were available. 
  37.  
  38. > Exceptions is the only way to go on this. And you shouldn't consider
  39.  
  40. The only way to view statements about "the only way" is with skepticism :-)
  41.  
  42. > them overkill, they really aren't. All you have to say is, for
  43. > example:
  44.  
  45. > try
  46. > {
  47. >     if ( action() == fail )
  48. >         throw( "dang file didn't open" ); 
  49. >             //Here I "throw" a char* pointer
  50. > }
  51. > catch( char* errorMessage ) // and here I catch it and deal with it
  52. > {
  53. >     printf( "%s", errorMessage );
  54. >     exit( -1 ); // or whatever
  55. > }
  56.  
  57. It is unlikely that the catch statement would be in the constructor.
  58. More than likely the catch would be in a piece of code that would
  59. do something about the problem.
  60.  
  61. > Try it a few times and you'll see how simple it is. The books make it
  62. > seem complicated but it isn't. The rewards are well worth it. 
  63.  
  64. Throwing exceptions is easy.  Writing code that behaves correctly
  65. when exceptions are thrown is not always so easy.  A recent issue
  66. of C++ Report discussed many of these potential problems.
  67.  
  68. > Exceptions will reliably trap and clean up a bad construction and I
  69. > recommend using them as such, however there are some other issues
  70. > involved.
  71.  
  72. Exceptions do not clean up anything.  The "stack unwinding" that
  73. is done in propagating an exception from its throw point to its
  74. handler will call the destructors of any automatic (stack based)
  75. objects that have been fully constructed since entry into the
  76. catch's try block.  Any clean up that is done must be done in
  77. those destructors.
  78.  
  79. > From my experience, I would say that any class whose constructor has
  80. > ANY chance to fail, should not do the failure-possible action during
  81. > the constructor. Have a function called init(void) (always void
  82. > argument) that does the actual activation of the object.
  83.  
  84. > Have the constructor do EVERYTHING necessary to prime/lock and load
  85. > the object, but the init( ) function actually pulls the trigger. The
  86. > init() function should just have (void) as an argument because all its
  87. > going to do is pull the trigger right?
  88.  
  89. > For a file object, the construction might set the name and file access
  90. > mode but the init() function would actually OPEN the file.
  91.  
  92. Dividing the initialization into two parts like this is a holdover
  93. from the days before exceptions.  This approach allowed you to
  94. get a return status from object initialization (via the init() func)
  95. while the constructor provided no direct error reporting means.
  96.  
  97. Doing things in this way means that when implementing the class,
  98. you need to keep track of whether init was called.  If one follows
  99. the convention that the constructor throws an exception if it cannot
  100. complete successfully, then the other member functions do not need
  101. to verify that initialization was performed.  (Otherwise there would
  102. be no object on which to call the member function.)
  103.  
  104. Using the construct/init separation also makes it more difficult
  105. to use the class as a member of another class.  If I do this, now
  106. either I have to use the construct/init separation, or I have to
  107. call the init functions of all my member objects that use the
  108. construct/init mechanism.
  109.  
  110. Things get even worse with multiple inheritance.  And with virtual
  111. inheritance you have to be careful that the init() functions of
  112. common base classes are not called more than once.
  113.  
  114. > This has many benefits. For example, if you're object's constructor
  115. > can fail, GLOBAL objects (those constructed at startup time) can fail
  116. > and you can't trap that with an try/catch exception handler. Way
  117. > bummer.
  118.  
  119. This is indeed a problem.  Global objects and exception-throwing
  120. constructors are not a good mix.  However, global objects sometimes are
  121. a problem because of order-of-initialization dependencies, and might be
  122. avoided for a couple of reasons.  One alternative to global objects is
  123. to provide global or public static functions that construct if necessary
  124. and return a reference.  This provides a way to catch exceptions and
  125. avoid order-of-initialization problems.
  126. --
  127. Kevin J. Hopps                  e-mail: kjhopps@mmm.com
  128. 3M Company                      phone:  (612) 737-4643
  129. 3M Center, Bldg. 235-2D-57      fax:    (612) 737-2700
  130. St. Paul, MN 55144-1000         Opinions are my own.  I don't speak for 3M.
  131.     But 3M speaks for me -- I did not write the following line:
  132.  
  133. Opinions expressed herein are my own and may not represent those of 3M.
  134.